﻿using System;
using System.Collections.Generic;
using SlimDX;

namespace InsTron
{
    class PlayerState
    {
        public Vector3 Position { get; private set; }
        public Vector3 Direction { get; private set; }
        private Vector3 turnDirection;
        public Vector3 TurnDirection { get { return turnDirection; } }
        public float TurnCooldown { get; private set; }
        public bool Boost { get; set; }
        public bool Brake { get; set; }
        public bool IsTurning { get; set; }
        public Vector3 FactualDirection
        {
            get
            {
                if (!IsTurning)
                    return Direction;
                Vector3 posFront = Position + Direction * TurnCooldown + TurnDirection * (float)Math.Sqrt(Math.Pow(Constants.TurningDistance, 2) - Math.Pow(TurnCooldown, 2));
                Vector3 diff = posFront - Position;
                diff.Normalize();
                return diff;
            }
        }
        

        public Vector3 GetFrontLeftCoordinates()
        {
            return GetFrontCoordinates(GetRotationAngle() - Constants.BikeFrontSideAngle);
        }


        public Vector3 GetFrontRightCoordinates()
        {
            return GetFrontCoordinates(GetRotationAngle() + Constants.BikeFrontSideAngle);
        }


        private Vector3 GetFrontCoordinates(float angle)
        {
            Vector4 rotated = Vector4.Transform(new Vector4(Constants.BikeDiagonalLength, 0.0f, 0.0f, 1.0f), Matrix.RotationY(angle));
            return new Vector3(rotated.X + Position.X, 0.0f, rotated.Z + Position.Z);
        }


        public PlayerState(int idx)
        {
            float dir = idx == 0 ? 1.0f : -1.0f;
            Position = new Vector3(Constants.StartingPositionShift * -dir, 0f, dir);
            Direction = new Vector3(dir, 0f, 0f);
            turnDirection = Direction;
            TurnCooldown = 0.0f;
            Boost = false;
            Brake = false;
        }


        public float GetSpeedMultiplier()
        {
            return Boost ? 
                Constants.BoostFactor : Brake ?
                    Constants.BrakeFactor : 1.0f;
        }


        public float MoveForward(float distance)
        {
            float moved;
            if (IsTurning)
            {
                moved = Math.Min(TurnCooldown, distance);
                TurnCooldown -= moved;
            }
            else
            {
                moved = distance;
            }
            Position += Direction * moved;
            return moved;
        }


        //private Vector3 GetPerpendicularDirection(bool left)
        //{
        //    Vector3 result = Direction;
        //    if (result.X == 0.0f)
        //    {
        //        result.X = result.Z;
        //        if (left)
        //            result.X = -result.X;
        //        result.Z = 0.0f;
        //    }
        //    else
        //    {
        //        result.Z = result.X;
        //        if (!left)
        //            result.Z = -result.Z;
        //        result.X = 0.0f;
        //    }
        //    return result;
        //}


        public void InitiateTurn(bool left)
        {
            IsTurning = true;
            TurnCooldown = Constants.TurningDistance;
            if (TurnDirection.X == 0.0f)
            {
                turnDirection.X = TurnDirection.Z;
                if (left)
                    turnDirection.X = -TurnDirection.X;
                turnDirection.Z = 0.0f;
            }
            else
            {
                turnDirection.Z = TurnDirection.X;
                if (!left)
                    turnDirection.Z = -TurnDirection.Z;
                turnDirection.X = 0.0f;
            }
        }


        public void ExecuteTurn()
        {
            Direction = TurnDirection;
            IsTurning = false;
        }


        private float GetRotationAngle()
        {
            float angle;
            if (FactualDirection.Z >= 0)
                angle = -(float)Math.Acos(FactualDirection.X);
            else
                angle = -(float)(Math.PI + Math.Acos(-FactualDirection.X));
            return angle;
        }


        public Matrix GetRotationMatrix()
        {
            return Matrix.RotationY(GetRotationAngle());
        }


        public Matrix GetTranslationMatrix()
        {
            return Matrix.Translation(Position);
        }
    }
}
